from image import MicrobitImage as Image
from machine import Pin, SoftI2C

i2c = SoftI2C(scl=Pin(1), sda=Pin(0), freq=100_000)

cmd = [
    [0xAE],  # SSD1306_DISPLAYOFF  关闭屏幕显示
    [0xA4],  # SSD1306_DISPLAYALLON_RESUME  恢复正常模式
    [0xD5, 0xC0],  # SSD1306_SETDISPLAYCLOCKDIV 设置显示时钟分频值，震荡频率: 越高刷新率越高，功耗一般也越高 10H-F0H
    [0xA8, 0x3F],  # SSD1306_SETMULTIPLEX 设置复用率
    [0xD3, 0x00],  # SSD1306_SETDISPLAYOFFSET 设置显示偏移
    [0 | 0x0],  # line #SSD1306_SETSTARTLINE
    [0x8D, 0x14],  # SSD1306_CHARGEPUMP 开启电荷泵
    [0x20, 0x00],  # SSD1306_MEMORYMODE 设置内存地址模式，水平地址模式,在读/写显示RAM之后，列地址指针增加
    [0x21, 0, 127],  # SSD1306_COLUMNADDR 设置列地址21h, 一共 128列
    [0x22, 0, 63],  # SSD1306_PAGEADDR 设置页面地址， 64行
    [0xA0 | 0x1],  # SSD1306_SEGREMAP 设置段重映射， 正常不左右反置
    [0xc8],  # SSD1306_COMSCANDEC 设置列输出扫描方向， C8是从上到下，C0是从下到上
    [0xDA, 0x12],  # SSD1306_SETCOMPINS  设置列引脚硬件配置
    [0x81, 0xCF],  # SSD1306_SETCONTRAST 设置对比度 【0-256】
    [0xD9, 0xF1],  # SSD1306_SETPRECHARGE 设置预充电周期
    [0xDB, 0x40],  # SSD1306_SETVCOMDETECT 设置VCOMH反压值
    [0xA6],  # SSD1306_NORMALDISPLAY 设置正常显示，像素点不反转
    [0xD6, 0],  # zoom set_power_off 设置放大模式， 0为不放大
    [0xAF]  # SSD1306_DISPLAYON 恢复屏幕显示
]

ADDR = 0x3C  # ssd1306的I2C地址
screen = bytearray(1025)  # send byte plus pixels 1306RAM空间 64 * 128 = 8192像素，8192/8 = 1024个字节，第一个字节保留屏幕起始位置，一共1025字节
screen[0] = 0x40  # 需要保留

class OLED1306(object):
    """基本描述

    OLED1306显示屏

    """

    def __init__(self):
        for c in cmd:
            self.__command(c)
            
    def __write_byte(self, data):
        i2c.writeto(ADDR, bytearray(data))

    def __command(self, c):
        i2c.writeto(ADDR, b'\x00' + bytearray(c))
        
    def set_power_on(self):
        """

        开启显示屏，默认开启

        """
        self.__command([0xAF])

    def set_power_off(self):
        """

        关闭显示屏，黑屏

        """
        self.__command([0xAE])    

    def __set_pos(self, col=0, page=0):
        '''
            指定要操作的像素点的坐标
            b0+y 是操作页地址的指令
            00-0F 设置列地址低四位
            10-1F 设置列地址高四位
            page [0-7]
            col  [0-127] 列地址由两个寄存器指定，分高四位和第四位，0x0n代表低四位，0x1m代表高四位
        '''
        self.__command([0xb0 | page])  # 设置显示行起始位置，行就是page，一共8行
        c1, c2 = col & 0x0F, col >> 4   # 列的低位和高位是拆开发送的，所以位运算切开
        self.__command([0x00 | c1])  # lower start column address  页地址模式下设置列起始地址低位
        self.__command([0x10 | c2])  # upper start column address  页地址模式下设置列起始地址高位

    def set_pixel(self, x, y, color=1):
        """

        点亮或熄灭一个像素点

        Args:
            x (number): X 轴  0-127
            y (number): Y 轴  0-63
            color (number): 1 点亮 0 熄灭

        Returns:
            NONE
        """
        page, shift_page = divmod(y, 8)  # 计算y的位置在第几页
        ind = x + page * 128 # 计算当前屏幕的起始位置字节序号,从上到下刷新，所以字节对应列方向上的值
        cur = screen[ind]
        if x == 0:
            cur = 0
        b = cur | (1 << shift_page) if color else screen[ind] & ~ (1 << shift_page)
        screen[ind] = b # 更新屏幕位置值
        self.__set_pos(x, page) # 更新光标位置
        i2c.writeto(ADDR, bytearray([0x40, b]))

    def set_clear(self, c=0):
        """
        设置整个屏幕熄灭或是点亮
        Args:
            c = 0 熄灭
            c = 1 点亮

        设置显示的所有像素点的值位0

        """
        global screen
        for i in range(1, 1025):
            screen[i] = c
        self.set_refresh()

    def set_refresh(self):
        """

        刷新显示

        """
        self.__set_pos()
        i2c.writeto(ADDR, screen)
        
    def draw_row(self, x, y, l, c=1):
        """

        画一横行

        Args:
            x (number): X 轴起始坐标 0-127
            y (number): Y 轴起始坐标 0-63
            l (number): 线段长度
            c (number): 1: 显示线段  2: 消除线段

        """
        d = 1 if l > 0 else -1
        for i in range(x, x + l, d):
            self.set_pixel(i, y, c)    

    def set_text(self, x, y, s):
        """

        显示一行文本,一行最多24个字符，每个字符 5 X 5 PX
        中间间隔

        Args:
            x (number): 第几个字符, 0 - 24
            y (number): Y 轴坐标 0 - 7
            s (str): 只接受字符串或字符类型参数,

        Returns:
            NONE
        """
        text_x_px = 10
        str_len = min(len(s), 12 - x)
        # print(len(s), str_len)
        for i in range(0, str_len):
            ch = Image(s[i])
            # print(ch, i, x)
            for c in range(0, text_x_px):
                col = 0
                ci = int(c / 2)
                for r in range(1, 6):
                    p = ch.get_pixel(ci, r - 1)  # 获取点阵字符的像素点
                    col = col | (1 << r) if (p != 0) else col
                ind = (x + i) * text_x_px + y * 128 + c
                # print('ind:', ind)
                screen[ind] = col
                screen[ind + 1] = col
        self.__set_pos(x * text_x_px, y)
        ind0 = x * text_x_px + y * 128
        i2c.write(ADDR, b"\x40" + screen[ind0 : ind + 1])
        
    def write_diagonal(self, x, y, l):
        col = x
        page = y
        v = 0x01
        if l > 8:
            raise ValueError("value need < 8")
        self.__set_pos(col, page)
        for i in range(0, l):
            self.__write_byte([0x40, v])
            v = v << 1
            col += 1
            
            
if __name__ == '__main__':
    print('start show')
    oled = OLED1306()

    oled.set_clear()
    oled.__write_byte([0x40, 0x0C,0x12,0x21,0x42,0x84,0x42,0x21,0x12,0x0C])
    oled.__set_pos(0,2)
    oled.__write_byte([0X40, 0xF3, 0xED,0xDE,0xBD,0x7B,0xBD,0xDE,0xED,0xF3])

